package comm;


import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.Queue;

// Log
// 每个线程一个日志文件，日志文件定期自动备份
// 可设置输出等级高于指定等级日志
class Log {
	static final int error = 1;
	static final int warn = 2;
	static final int info = 3;
	static final int debug_off = 4;
	static final int debug_error = 5;
	static final int debug_warn = 6;
	static final int debug_info = 7;
	static final int debug = 8;
	private int currentLevel; // 当前日志输出等级，仅输出级别号小于此值的日志
	private Queue<String> logBuffer = new LinkedList<String>(); // ??大小控制

	private String prevStr = "log"; // 用于实现两条内容完全相同的日志不重复打。初始值为随便的一个string，防止后边print中equal的空引用异常

	static final SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
	static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");

	// static final SimpleDateFormat folderFormat = new
	// SimpleDateFormat("yyyy-MM-W");
	
	static final String defaultLogFileName = "Log-CommServer"; // Log静态函数输出日志时使用到默认文件名
	static final String defaultLogFileNameTemp = "Log-CommServer-Temp"; // Log静态函数输出日志时使用到默认文件名（临时文件）

	Log() {
		int num = PropertyManager.getInt("LogLevel", 8);
		if (num < 0) {
			num = 8;
		}
		this.currentLevel = num;
	}

	// 获取当前日志等级
	int getLevel() {
		return this.currentLevel;
	}

	// 修改当前日志等级
	void setLevel(final int level) {
		if (level >= 0) {
			this.currentLevel = level;
		}
	}

	// 获取log缓冲大小，由LogFileManager调
	int getBufferSize() {
		return logBuffer.size();
	}

	// 获取一条log，由LogFileManager调
	String getOneLog() {
		String log = logBuffer.poll();
		if (null == log) {
			this.logBuffer.clear();
		}
		return log;
	}

	void print(final int level, final String info) {
		if (level > this.currentLevel || null == info) {
			return;
		}
		if (false == info.equals(prevStr)) {
			this.prevStr = info;
			String time = Log.timeFormat.format(new java.util.Date());
			String str = String.format("%s %d %s %s\n", time, level, info, getTraceInfo(1));
			logBuffer.offer(str);
		}

	}

	// debug 输出一行string
	// // 供调试时查询用，正常运行时关闭
	public static void println(String str) {
		Log.println(str, Log.defaultLogFileName, 1, true);
	}

	public static void println(String str, String logName) {
		Log.println(str, logName, 1, true);
	}

	public static void println(String str, String logName, boolean aAppend) {
		Log.println(str, logName, 1, aAppend);
	}
	
	public static void println(Exception aException) {
		Log.println(aException, Log.defaultLogFileName, 1);
	}

	public static void println(Exception aException, String logName) {
		Log.println(aException, logName, 1);
	}

	private static void println(Exception aException, String logName, int aDepth) {
		try {
			aException.printStackTrace();
			StackTraceElement[] elems = aException.getStackTrace();
			StringBuffer strbuf = new StringBuffer();
			strbuf.append(aException.toString());
			for (int i = 0; i < elems.length; i++) {
				strbuf.append("\n\tat " + elems[i]);
			}
			Log.println(strbuf.toString(), logName, aDepth + 1, true);
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	// aAppend为true表示日志已追加方式增长，false表示日志以覆盖方式增长（仅保留最后一条日志）
	private static void println(String str, String logName, int aDepth, boolean aAppend) {
		boolean state = true;
		// state = false;
		if (true == state) {
			final java.util.Date newDate = new java.util.Date();
			final String curTime = Log.timeFormat.format(newDate);
			final String curDate = Log.dateFormat.format(newDate);
			// final String curFolder = Log.folderFormat.format(newDate);
			final String strLog = curTime + " " + str + "--" + getTraceInfo(aDepth + 1) + "\n";

			// ??代码非线程安全，可能涉及多个线程同时操作同一文件，涉及并发可能会出问题，照成的结果可能时丢日志，不严重
			try {
				OutputStreamWriter fis = new OutputStreamWriter(new FileOutputStream("./logfile/" + curDate + logName, aAppend), PropertyManager.CharSet);
				fis.write(strLog);
				fis.close();
				fis = null;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

	// debug 输出byte数组内容
	public static void printBytes(byte[] aBuffer, int aOffset, int aLength) {
		StringBuilder strbuf = new StringBuilder();
		strbuf.append("Byte数组: ");

		int length = aLength;
		if (aBuffer.length - aOffset < aLength)
			length = aBuffer.length - aOffset;
		for (int i = aOffset; i < aOffset + length; i++) {
			strbuf.append(Integer.toHexString((int) (aBuffer[i] & 0xff)) + ";");
		}
		Log.println(strbuf.toString(), Log.defaultLogFileName, 1, true);

	}

	// 获取调用者的信息
	// aDepth首个关注的调用者到当前函数调用层数差值，用来获取首个关注的调用者的位置
	private static String getTraceInfo(int aDepth) {
		StringBuilder sb = new StringBuilder();
		StackTraceElement[] stacks = new Throwable().getStackTrace();
		if (stacks.length > aDepth + 1) {
			StackTraceElement ste = stacks[aDepth + 1]; // 绝对定位到上上一个调用者
			sb.append("--").append(ste.getClassName()).append(".").append(ste.getMethodName()).append("--").append(ste.getFileName()).append(":").append(ste.getLineNumber());
			return sb.toString();
		} else {
			return null;
		}
	}

	// 获取调用者信息（栈信息）
	static String getTraceList() {
		StringBuilder strbuf = new StringBuilder();
		StackTraceElement[] stacks = new Throwable().getStackTrace();
		strbuf.append(" TraceList:");
		if (stacks.length > 1) {
			for (int i = 1; i < stacks.length; i++) {
				strbuf.append("\n\tat " + stacks[i]);
			}
		}
		return strbuf.toString();
	}

}

class LogOutputManager {
	boolean isLog;
	private String logName;
	final String filePath = "./logfile/";

	LogOutputManager(String aLogName) {
		this.logName = aLogName;
		this.isLog = true;
	}

	void setState(final boolean aIsLog) {
		this.isLog = aIsLog;
	}

	// 日志写入文件
	void printToFile(final Log log) {
		if (false == this.isLog || null == log || log.getBufferSize() <= 0) {
			return;
		}

		try {
			// 格式化文件名，实现自动定时备份
			String fileFullName = Log.dateFormat.format(new java.util.Date()) + this.logName;

			// 日志写入文件
			OutputStreamWriter fis = new OutputStreamWriter(new FileOutputStream(filePath + fileFullName, true), PropertyManager.CharSet);
			int logNum = log.getBufferSize();
			String str = null;
			while ((logNum--) > 0) {
				str = log.getOneLog();
				if (null != str) {
					fis.write(str);
					fis.flush();
				}
				str = null;
			}
			fis.close();
			fis = null;
		} catch (Exception e) {
			e.printStackTrace();
			Log.println(e.toString());
		}
	}
}


// ** 待改进

// 考虑加入短时间内不重复写内容相同日志？？
// // 目前仅加入不写入与上次内容完全相同日志

// ** 说明

// Log 中到buffer用Queue实现，可能并发性不好，Log中涉及到多个线程的操作，若有问题可改为同步的ConcurrentLinkedQueue

// 注：一些string操作可用StringBuffer（线程安全），StringBuilder（单线程）